<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>定时器</title>
</head>
<body>
</body>
<script>
    /**定时器
     * 1.JavaScript 在浏览器中是单线程执行的，但允许使用定时器指定在某个时间之后或每隔一段时间就执行相应的代码.
     * 2.setTimeout()用于指定在一定时间后执行某些代码，而 setInterval()用于指定每隔一段时间执行某些代码.
     * 3.setTimeout()方法通常接收两个参数：要执行的代码和在执行回调函数前等待的时间（毫秒）。第一个参数可以是包
     *   含 JavaScript 代码的字符串（类似于传给 eval()的字符串）或者一个函数，第二个参数是要等待的毫秒数，而不
     *   是要执行代码的确切时间，JavaScript 是单线程的，所以每次只能执行一段代码。为了调度不同代码的执行，JavaScript
     *   维护了一个任务队列。其中的任务会按照添加到队列的先后顺序执行。setTimeout()的第二个参数只是告诉 JavaScript
     *   引擎在指定的毫秒数过后把任务添加到这个队列。如果队列是空的，则会立即执行该代码。如果队列不是空的，则代码必须等待
     *   前面的任务执行完才能执行。
     * 4.所有超时执行的代码（函数）都会在全局作用域中的一个匿名函数中运行，因此函数中的 this 值在非严格模式下始终指向 window，
     *   而在严格模式下是 undefined。如果给 setTimeout()提供了一个箭头函数，那么 this 会保留为定义它时所在的词汇作用域。
     * 5.setInterval: 指定的任务会每隔指定时间就执行一次，直到取消循环定时或者页面卸载。setInterval()同样可以接收两个参数：
     *   要执行的代码（字符串或函数），以及把下一次执行定时代码的任务添加到队列要等待的时间（毫秒），第二个参数，也就是间隔时间，
     *   指的是向队列添加新任务之前等待的时间。比如，调用 setInterval()的时间为 01:00:00，间隔时间为 3000 毫秒。这意味着
     *   01:00:03 时，浏览器会把任务添加到执行队列。浏览器不关心这个任务什么时候执行或者执行要花多长时间。因此，到了 01:00:06，
     *   它会再向队列中添加一个任务。由此可看出，执行时间短、非阻塞的回调函数比较适合 setInterval()。
     */
        // setTimeout
        //1.采用eval字符串
        // setTimeout("console.log('我是字符串')",1000); 经过webstorm实践发现，采用该方法报错
        //2.采用函数
        // setTimeout(function(){console.log('我是函数')},1000);
        //3.取消超时任务
        // let timer = setTimeout(function(){//2s后将超时任务放入任务队列
        //     console.log('我是超时任务')},2000);
        // setTimeout(function (){clearTimeout(timer)},1000);//1s后取消超时任务
        //4.setTimeout中的this
        var username = 'smith'
        let person = {
            age:25,
            username:'silly',
            showThis:function(){
                //对于非箭头函数定义的this,谁调用这个函数，就指向谁，可以知道，此处的this指向 person 对象
                console.log(this);
                setTimeout(function(){
                    //对于 setTimeout 内的函数，可以知道是 function(){} 函数是由 setTimeout 函数调用的，而
                    // setTimeout 函数是由 window 调用的，所以，this 指向 window.
                    console.log('普通函数的this:'+ this);
                    console.log('普通函数的this:'+ this.username);
                },1000)
                setTimeout(()=>{
                    //箭头函数内部的this，由定义箭头函数时，所处的执行上下文环境所决定的，可以尝试将箭头函数定义到
                    // setTimeout外部的一个变量中，上送这个变量给setTimeout，此时可以很明显的知道，此处的this与开始的
                    //this指向同一个内容
                    console.log('箭头函数中的this:'+this);
                    console.log('箭头函数中的this:'+this.username);
                },1000)
            }
        }
        person.showThis();
        // {age: 25, username: 'silly', showThis: ƒ}
        // 普通函数的this:[object Window]
        // 普通函数的this:smith
        // 箭头函数中的this:[object Object]
        // 箭头函数中的this:silly
        let show = person.showThis;
        show();//解释同上
        // [object Window]
        // 普通函数的this:[object Window]
        // 普通函数的this:smith
        // 箭头函数中的this:[object Window]
        // 箭头函数中的this:smith

    //setInterval
    //1.setInterval的取消(在这个例子中，变量 num 会每半秒递增一次，直至达到最大限制值。此时循环定时会被取消。)
      let intervalId = null;
      let max = 10;
      let num = 0;
    intervalId = setInterval(function(){
        console.log('setInterval--'+num);
          if(num === max){
              // 如果达到最大值，则取消所有未执行的任务
              clearInterval(intervalId);
              alert('done')
          } else {
              num++;
          }
      },500);
    //2.采用 setTimeout 实现
    let max1 = 10;
    let num1 = 0;
    let incrementNumber = function(){
        num1++;
        //如果还没有达到最大值，再设置一个超时任务
        if(num1 < max1){
            console.log('setTimeout--'+num1);
            setTimeout(incrementNumber,1000)
        } else {
            alert('done!!')
        }
    }
    setTimeout(incrementNumber,1000)
    // 注意在使用 setTimeout()时，不一定要记录超时 ID，因为它会在条件满足时自动停止，否则会自动设置另一个超时任务。
    // 这个模式也是设置循环任务的推荐做法
    //3.生产中存在的问题：
    //  setInterval()在实践中很少会在生产环境下使用，因为一个任务结束和下一个任务开始之间的时间间隔是无法保证的，
    //  有些循环定时任务可能会因此而被跳过。而像前面这个例子中一样使用 setTimeout()则能确保不会出现这种情况。
    //  一般来说，最好不要使用 setInterval()。(总体而言，采用setTimeout能保证只有上一次任务执行完成之后，才会执行下一次任务，
    //  而setInterval无法保证这一点)。
</script>
</html>

